//
// Created by denglibin on 2021/4/2.
//
#include "shader.h"
#include <stdio.h>
#include <stdlib.h>


/**
 * 创建顶点着色器
 * @param vertexShaderSource
 * @return
 */
static GLuint createVertexShader(const char** vertexShaderSource)
{
    // 用glCreateShader创建顶点着色器
    GLuint vertexShader;
    vertexShader = glCreateShader(GL_VERTEX_SHADER); //需要创建的着色器类型以参数形式提供给glCreateShader, 创建一个顶点着色器，传递的参数是GL_VERTEX_SHADER
    //把这个着色器源码附加到着色器对象上，然后编译它
    //要编译的着色器对象作为第一个参数。第二参数指定了传递的源码字符串数量，这里只有一个。第三个参数是顶点着色器真正的源码，第四个参数我们先设置为NULL
    glShaderSource(vertexShader, 1, vertexShaderSource, NULL);
    //编译着色器程序
    glCompileShader(vertexShader);

    //检测编译是否成功
    GLint success;
    GLchar infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED: %s\n", infoLog);
        exit(1);
    }
    return vertexShader;
}
/**
 * 创建片段着色器
 */
static GLuint createFragmentShader(const char** fragmentShaderSource)
{
    //片段着色器(计算像素最后的颜色输出)
    //在计算机图形中颜色被表示为有4个元素的数组：红色、绿色、蓝色和alpha(透明度)分量，通常缩写为RGBA。当在OpenGL或GLSL中定义一个颜色的时候，我们把颜色每个分量的强度设置在0.0到1.0之间。比如说我们设置红为1.0f，绿为1.0f，我们会得到两个颜色的混合色，即黄色。这三种颜色分量的不同调配可以生成超过1600万种不同的颜色！

    //创建片段着色器对象
    GLuint fragmentShader;
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    //编译片段着色器源码
    glShaderSource(fragmentShader, 1, (const GLchar *const *) fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    //检查是否编译成功
    GLint success;
    GLchar infoLog[512];
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED:%s\n", infoLog);
        exit(1);
        return 0;
    }
    return fragmentShader;
}

/**
 * 读取文件字符串
 * @param filePath
 * @return
 */
static  char * readeFile(const char * filePath){
    FILE *fp;

    if( (fp=fopen(filePath, "r")) == NULL ){
        printf("Fail to open file: %s\n", filePath);
        exit(0);
    }
    fseek(fp, 0, SEEK_END); //定位到文件末
    int fileLen = ftell(fp); //文件长度
    fseek(fp, 0, SEEK_SET); //恢复到文件头
    int bufferSize = fileLen / 8 * 8 + 8;
    char * buf = calloc(bufferSize, sizeof (char ));
    if (buf == NULL){
        printf("calloc fail\n");
        exit(0);
    }
    fread(buf, sizeof(char ), bufferSize, fp);
    fclose(fp);
    return buf;
}

/**
 * 链接着色器
 * @param vertexShader 顶点着色器
 * @param fragmentShader 片段着色器
 */
static void linkProgram(GLuint shaderProgram)
{

    glLinkProgram(shaderProgram);
    //检查是否链接成功
    GLint success;
    GLchar infoLog[512];

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success)
    {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        exit(1);
    }

}


/**
 * 编译着色器程序
 * @param vertexPath 顶点着色器源码文件
 * @param fragmentPath 片段着色器源码文件
 */
GLuint compileShaderSource(const char*vertexPath, const char* fragmentPath){
    GLuint shaderProgram  = glCreateProgram();
    char * vertexSource = readeFile(vertexPath);
    char * fragmentSource = readeFile(fragmentPath);
    GLuint  vertex = createVertexShader((const char **) &vertexSource);
    GLuint  fragment = createFragmentShader((const char **) &fragmentSource);
    glAttachShader(shaderProgram, vertex);
    glAttachShader(shaderProgram, fragment);
    linkProgram(shaderProgram);
    free(vertexSource);
    free(fragmentSource);

    // 删除着色器，它们已经链接到我们的程序中了，已经不再需要了
    glDeleteShader(vertex);
    glDeleteShader(fragment);
    return shaderProgram;
}
void setUniformVec3(GLuint shaderProgram, const char * varName, vec3 vec){
    unsigned int loc = glGetUniformLocation(shaderProgram, varName);
    glUniform3fv(loc, 1, vec);
}

void setUniformFloat(GLuint shaderProgram, const char * varName, float value){
    unsigned int loc = glGetUniformLocation(shaderProgram, varName);
    glUniform1f(loc, value);
}